//
//------------------------------------------------------------------------------
//   Copyright 2007-2010 Mentor Graphics Corporation
//   Copyright 2007-2011 Cadence Design Systems, Inc. 
//   Copyright 2010 Synopsys, Inc.
//   All Rights Reserved Worldwide
//
//   Licensed under the Apache License, Version 2.0 (the
//   "License"); you may not use this file except in
//   compliance with the License.  You may obtain a copy of
//   the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in
//   writing, software distributed under the License is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//   CONDITIONS OF ANY KIND, either express or implied.  See
//   the License for the specific language governing
//   permissions and limitations under the License.
//------------------------------------------------------------------------------

`ifndef UVM_REPORT_SERVER_SVH
`define UVM_REPORT_SERVER_SVH

typedef class uvm_report_object;

//------------------------------------------------------------------------------
//
// CLASS: uvm_report_server
//
// uvm_report_server is a global server that processes all of the reports
// generated by an uvm_report_handler. None of its methods are intended to be
// called by normal testbench code, although in some circumstances the virtual
// methods process_report and/or compose_uvm_info may be overloaded in a
// subclass.
//
//------------------------------------------------------------------------------

typedef class uvm_report_catcher;
class uvm_report_server extends uvm_object;

  local int max_quit_count; 
  local int quit_count;
  local int severity_count[uvm_severity];

  // Needed for callbacks
  function string get_type_name();
    return "uvm_report_server";
  endfunction
 
  // Variable: id_count
  //
  // An associative array holding the number of occurences
  // for each unique report ID.

  protected int id_count[string];

  bit enable_report_id_count_summary=1;


  // Function: new
  //
  // Creates an instance of the class.

  function new();
    set_name("uvm_report_server");
    set_max_quit_count(0);
    reset_quit_count();
    reset_severity_counts();
  endfunction


  static protected uvm_report_server m_global_report_server = get_server();

  // Function: set_server
  //
  // Sets the global report server to use for reporting. The report
  // server is responsible for formatting messages.

  static function void set_server(uvm_report_server server);
    if(m_global_report_server != null) begin
      server.set_max_quit_count(m_global_report_server.get_max_quit_count());
      server.set_quit_count(m_global_report_server.get_quit_count());
      m_global_report_server.copy_severity_counts(server);
      m_global_report_server.copy_id_counts(server);
    end

    m_global_report_server = server;
  endfunction


  // Function: get_server
  //
  // Gets the global report server. The method will always return 
  // a valid handle to a report server.

  static function uvm_report_server get_server();
    if (m_global_report_server == null)
      m_global_report_server = new;
    return m_global_report_server;
  endfunction

  local bit m_max_quit_overridable = 1;

  // Function: set_max_quit_count

  function void set_max_quit_count(int count, bit overridable = 1);
    if (m_max_quit_overridable == 0) begin
      uvm_report_info("NOMAXQUITOVR", $sformatf("The max quit count setting of %0d is not overridable to %0d due to a previous setting.", max_quit_count, count), UVM_NONE);
      return;
    end
    m_max_quit_overridable = overridable;
    max_quit_count = count < 0 ? 0 : count;
  endfunction

  // Function: get_max_quit_count
  //
  // Get or set the maximum number of COUNT actions that can be tolerated
  // before an UVM_EXIT action is taken. The default is 0, which specifies
  // no maximum.

  function int get_max_quit_count();
    return max_quit_count;
  endfunction


  // Function: set_quit_count

  function void set_quit_count(int quit_count);
    quit_count = quit_count < 0 ? 0 : quit_count;
  endfunction

  // Function: get_quit_count

  function int get_quit_count();
    return quit_count;
  endfunction

  // Function: incr_quit_count

  function void incr_quit_count();
    quit_count++;
  endfunction

  // Function: reset_quit_count
  //
  // Set, get, increment, or reset to 0 the quit count, i.e., the number of
  // COUNT actions issued.

  function void reset_quit_count();
    quit_count = 0;
  endfunction

  // Function: is_quit_count_reached
  //
  // If is_quit_count_reached returns 1, then the quit counter has reached
  // the maximum.

  function bit is_quit_count_reached();
    return (quit_count >= max_quit_count);
  endfunction


  // Function: set_severity_count

  function void set_severity_count(uvm_severity severity, int count);
    severity_count[severity] = count < 0 ? 0 : count;
  endfunction

  // Function: get_severity_count

  function int get_severity_count(uvm_severity severity);
    return severity_count[severity];
  endfunction

  // Function: incr_severity_count

  function void incr_severity_count(uvm_severity severity);
    severity_count[severity]++;
  endfunction

  // Function: reset_severity_counts
  //
  // Set, get, or increment the counter for the given severity, or reset
  // all severity counters to 0.

  function void reset_severity_counts();
    uvm_severity_type s;
    s = s.first();
    forever begin
      severity_count[s] = 0;
      if(s == s.last()) break;
      s = s.next();
    end
  endfunction


  // Function: set_id_count

  function void set_id_count(string id, int count);
    id_count[id] = count < 0 ? 0 : count;
  endfunction

  // Function: get_id_count

  function int get_id_count(string id);
    if(id_count.exists(id))
      return id_count[id];
    return 0;
  endfunction

  // Function: incr_id_count
  //
  // Set, get, or increment the counter for reports with the given id.

  function void incr_id_count(string id);
    if(id_count.exists(id))
      id_count[id]++;
    else
      id_count[id] = 1;
  endfunction


  // f_display
  //
  // This method sends string severity to the command line if file is 0 and to
  // the file(s) specified by file if it is not 0.

  function void f_display(UVM_FILE file, string str);
    if (file == 0)
      $display("%s", str);
    else
      $fdisplay(file, "%s", str);
  endfunction


  // Function- report
  //
  //

  virtual function void report(
      uvm_severity severity,
      string name,
      string id,
      string message,
      int verbosity_level,
      string filename,
      int line,
      uvm_report_object client
      );
    string m;
    uvm_action a;
    UVM_FILE f;
    bit report_ok;
    uvm_report_handler rh;

    rh = client.get_report_handler();
  
    // filter based on verbosity level
 
    if(!client.uvm_report_enabled(verbosity_level, severity, id)) begin
       return;
    end

    // determine file to send report and actions to execute

    a = rh.get_action(severity, id); 
    if( uvm_action_type'(a) == UVM_NO_ACTION )
      return;

    f = rh.get_file_handle(severity, id);

    // The hooks can do additional filtering.  If the hook function
    // return 1 then continue processing the report.  If the hook
    // returns 0 then skip processing the report.

    if(a & UVM_CALL_HOOK)
      report_ok = rh.run_hooks(client, severity, id,
                              message, verbosity_level, filename, line);
    else
      report_ok = 1;

    if(report_ok)
      report_ok = uvm_report_catcher::process_all_report_catchers(
                     this, client, severity, name, id, message,
                     verbosity_level, a, filename, line);

    if(report_ok) begin	
      m = compose_message(severity, name, id, message, filename, line); 
      process_report(severity, name, id, message, a, f, filename,
                     line, m, verbosity_level, client);
    end
  
  endfunction



  // Function: process_report
  //
  // Calls <compose_message> to construct the actual message to be
  // output. It then takes the appropriate action according to the value of
  // action and file. 
  //
  // This method can be overloaded by expert users to customize the way the
  // reporting system processes reports and the actions enabled for them.

  virtual function void process_report(
      uvm_severity severity,
      string name,
      string id,
      string message,
      uvm_action action,
      UVM_FILE file,
      string filename,
      int line,
      string composed_message,
      int verbosity_level,
      uvm_report_object client
      );
    // update counts
    incr_severity_count(severity);
    incr_id_count(id);

    if(action & UVM_DISPLAY)
      $display("%s",composed_message);

    // if log is set we need to send to the file but not resend to the
    // display. So, we need to mask off stdout for an mcd or we need
    // to ignore the stdout file handle for a file handle.
    if(action & UVM_LOG)
      if( (file == 0) || (file != 32'h8000_0001) ) //ignore stdout handle
      begin
        UVM_FILE tmp_file = file;
        if( (file&32'h8000_0000) == 0) //is an mcd so mask off stdout
        begin
           tmp_file = file & 32'hffff_fffe;
        end
        f_display(tmp_file,composed_message);
      end    

    if(action & UVM_EXIT) client.die();

    if(action & UVM_COUNT) begin
      if(get_max_quit_count() != 0) begin
          incr_quit_count();
        if(is_quit_count_reached()) begin
          client.die();
        end
      end  
    end

    if (action & UVM_STOP) $stop;

  endfunction



  // Function: compose_message
  //
  // Constructs the actual string sent to the file or command line
  // from the severity, component name, report id, and the message itself. 
  //
  // Expert users can overload this method to customize report formatting.

  virtual function string compose_message(
      uvm_severity severity,
      string name,
      string id,
      string message,
      string filename,
      int    line
      );
    uvm_severity_type sv;
    string time_str;
    string line_str;
    
    sv = uvm_severity_type'(severity);
    $swrite(time_str, "%0t", $realtime);
 
    case(1)
      (name == "" && filename == ""):
	       return {sv.name(), " @ ", time_str, " [", id, "] ", message};
      (name != "" && filename == ""):
	       return {sv.name(), " @ ", time_str, ": ", name, " [", id, "] ", message};
      (name == "" && filename != ""):
           begin
               $swrite(line_str, "%0d", line);
		 return {sv.name(), " ",filename, "(", line_str, ")", " @ ", time_str, " [", id, "] ", message};
           end
      (name != "" && filename != ""):
           begin
               $swrite(line_str, "%0d", line);
	         return {sv.name(), " ", filename, "(", line_str, ")", " @ ", time_str, ": ", name, " [", id, "] ", message};
           end
    endcase
  endfunction 




  // Function: summarize
  //
  // See <uvm_report_object::report_summarize> method.

  virtual function void summarize(UVM_FILE file=0);
    string id;
    string name;
    string output_str;
    uvm_report_catcher::summarize_report_catcher(file);
    f_display(file, "");
    f_display(file, "--- UVM Report Summary ---");
    f_display(file, "");

    if(max_quit_count != 0) begin
      if ( quit_count >= max_quit_count ) f_display(file, "Quit count reached!");
      $sformat(output_str, "Quit count : %5d of %5d",
                             quit_count, max_quit_count);
      f_display(file, output_str);
    end

    f_display(file, "** Report counts by severity");
    for(uvm_severity_type s = s.first(); 1; s = s.next()) begin
      if(severity_count.exists(s)) begin
        int cnt;
        cnt = severity_count[s];
        name = s.name();
        $sformat(output_str, "%s :%5d", name, cnt);
        f_display(file, output_str);
      end
      if(s == s.last()) break;
    end

    if (enable_report_id_count_summary) begin

      f_display(file, "** Report counts by id");
      for(int found = id_count.first(id);
           found;
           found = id_count.next(id)) begin
        int cnt;
        cnt = id_count[id];
        $sformat(output_str, "[%s] %5d", id, cnt);
        f_display(file, output_str);
      end

    end

  endfunction


  // Function: dump_server_state
  //
  // Dumps server state information.

  function void dump_server_state();

    string s;
    uvm_severity_type sev;
    string id;

    f_display(0, "report server state");
    f_display(0, "");   
    f_display(0, "+-------------+");
    f_display(0, "|   counts    |");
    f_display(0, "+-------------+");
    f_display(0, "");

    $sformat(s, "max quit count = %5d", max_quit_count);
    f_display(0, s);
    $sformat(s, "quit count = %5d", quit_count);
    f_display(0, s);

    sev = sev.first();
    forever begin
      int cnt;
      cnt = severity_count[sev];
      s = sev.name();
      $sformat(s, "%s :%5d", s, cnt);
      f_display(0, s);
      if(sev == sev.last())
        break;
      sev = sev.next();
    end

    if(id_count.first(id))
    do begin
      int cnt;
      cnt = id_count[id];
      $sformat(s, "%s :%5d", id, cnt);
      f_display(0, s);
    end
    while (id_count.next(id));

  endfunction


  // Function- copy_severity_counts
  //
  // Internal method.

  function void copy_severity_counts(uvm_report_server dst);
    foreach(severity_count[s]) begin
      dst.set_severity_count(s,severity_count[s]);
    end
  endfunction


  // Function- copy_severity_counts
  //
  // Internal method.

  function void copy_id_counts(uvm_report_server dst);
    foreach(id_count[s]) begin
      dst.set_id_count(s,id_count[s]);
    end
  endfunction


endclass



//----------------------------------------------------------------------
// CLASS- uvm_report_global_server
//
// Singleton object that maintains a single global report server
//----------------------------------------------------------------------
class uvm_report_global_server;

  function new();
    void'(get_server());
  endfunction


  // Function: get_server
  //
  // Returns a handle to the central report server.

  function uvm_report_server get_server();
    return uvm_report_server::get_server();
  endfunction


  // Function- set_server (deprecated)
  //
  //

  function void set_server(uvm_report_server server);
    uvm_report_server::set_server(server);
  endfunction

endclass


`endif // UVM_REPORT_SERVER_SVH
